using System;
using System.Collections.Generic;
using System.Text;

namespace PCRSim
{
  enum CommandNumberEnum : byte
  {
    powerUp = 0x00,
    powerDown = 0x01,
    getRadioID = 0x31,
    setMute = 0x13,
    changeChannel = 0x10, // first data byte is 02,
    getChannelInfo = 0x25,
    getExtendedChannelInfo = 0x22,
    getSignalData = 0x43,
    monitorChannelData = 0x50,
    modeListen = 0x74
  }

  enum ResponseNumberEnum : byte
  {
    powerUp = 0x80,
    powerDown = 0x81,
    getRadioID = 0xB1,
    setMute = 0x93,
    changeChannel = 0x90,
    getChannelInfo = 0xA5,
    getExtendedChannelInfo = 0xA2,
    getSignalData = 0xC3,
    listenMode = 0xF5
  }

  class Response
  {
    Random rand = new Random();
    ASCIIEncoding enc = new ASCIIEncoding();

    public byte[] buffer = new byte[1024];
    int dataLength;
    byte channel;
    byte nextChannel = 0;
    const byte maxChannel = 20;

    int[] songIndex = new int[maxChannel + 1];
    DateTime[] changeTime = new DateTime[maxChannel + 1];

    List<String> titles = new List<string>();
    List<String> artists = new List<string>();

    Random r = new Random();
    public Response()
    {
      string tmpStr;
      System.IO.TextReader inFile = new System.IO.StreamReader("songs.txt");
      tmpStr = inFile.ReadLine();
      while (tmpStr != null)
      {
        string[] parts = tmpStr.Split('\\');
        artists.Add(parts[0]);
        titles.Add(parts[parts.Length - 1]);
        tmpStr = inFile.ReadLine();
      }
      inFile.Close();
    }

    public void ReadCommand(System.IO.Ports.SerialPort port)
    {
      int len = port.BytesToRead;
      while (len >= 7)
      {
        // keep reading data until we reach the beginning of a data block
        port.Read(buffer, 0, 1);
        if (buffer[0] != 0x5a) break;
        port.Read(buffer, 1, 1);
        if (buffer[1] != 0xa5) break;

        port.Read(buffer, 2, 2);
        dataLength = buffer[2] * 256 + buffer[3];
        port.Read(buffer, 4, (int) dataLength + 2);
        ParseCommand(port);
      }

    }

    private void ParseCommand(System.IO.Ports.SerialPort port)
    {
      CommandNumberEnum cmd = (CommandNumberEnum)buffer[4];

      switch (cmd)
      {
        case CommandNumberEnum.powerUp:
          write(port, powerUp());
          Console.WriteLine("Power Up");
          break;
        case CommandNumberEnum.powerDown:
          write(port, powerDown());
          Console.WriteLine("Power Down");
          break;
        case CommandNumberEnum.getChannelInfo:
          write(port, getChannelInfo(buffer));
          break;
        case CommandNumberEnum.changeChannel:
          write(port, changeChannel(buffer));
          Console.WriteLine("Changed Channel: " + channel.ToString());
          break;
        case CommandNumberEnum.getSignalData:
          write(port, getSignalData());
          //Console.WriteLine("Requested Signal Data");
          break;
        default:
          displayBuffer(">", buffer);
          Console.WriteLine("Not handled: " + cmd.ToString("x"));
          break;
      }
    }

    private byte[] powerUp()
    {
      return new byte[] { 
        (byte) ResponseNumberEnum.powerUp,
        0x00,
        0x00,
        0x01, 0x01, 0x20, 0x06,
        0x00,
        0x01, 0x01, 0x20, 0x06,
        0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41
      };
    }

    private byte[] powerDown()
    {
      return new byte[] { 
        (byte) ResponseNumberEnum.powerDown
      };
    }

    private void writeString(byte[] array, int index, String text, int maxLength)
    {
      int len = text.Length;
      if (len > maxLength) len = maxLength;
      Array.Copy(enc.GetBytes(text), 0, array, index, len);
    }

    private byte[] getChannelInfo(byte[] data)
    {
      nextChannel = data[6];
      if (data[5] == 9)
      {
        if (nextChannel < maxChannel)
          nextChannel += 1;
        else
          nextChannel = 0;
      }
      else
      {
        Console.WriteLine("Requested info for " + nextChannel);
      }

      int i = songIndex[nextChannel];
      if (DateTime.Now > changeTime[nextChannel])
      {
        i = r.Next(titles.Count);
        songIndex[nextChannel] = i;
        changeTime[nextChannel] = DateTime.Now.AddSeconds(r.Next(30));
      }

      byte[] rdata = new byte[74];
      rdata[0] = 0xa5;
      rdata[3] = nextChannel;
      rdata[4] = 0x01;
      rdata[5] = 1;
      writeString(rdata, 6, "Channel " + nextChannel.ToString(), 16);

      rdata[22] = 1;
      writeString(rdata, 24, "Genre " + nextChannel.ToString(), 16);

      rdata[40] = 1;
      writeString(rdata, 41, artists[i], 16);

      rdata[57] = 1;
      writeString(rdata, 58, titles[i], 16);

      return rdata;
    }

    private byte[] getSignalData()
    {
      byte[] rdata = new byte[] {
      0xC3,0,0,
      0x03, 0x03,0x3,      // sat signal status, antenna status, terrestrial signal status
      0x01,0x02,0x03,       // sat 1 QPSK, sat2 QPSK, ter MCM
      0x04,0x05,0x06,       // Sat1 TDM, Sat2 TDM, Ter TDM 
      0x00,0x00,            // sat1 BER
      0x00,0x00,            // sat2 BER
      0x00,0x00,            // Ter BER
      0x00, 0x00,0x00,0x00, // Sat AGC, Ter AGC, Sat1 CN, Sat2 CN
      0x00,0x00,0x00,0x00,0x00,0x00};
      return rdata;
    }

    private byte[] artistChanged()
    {
      byte[] rdata = new byte[35];
      rdata[0] = 0xd4;
      rdata[1] = 1; // (byte)rand.Next(maxChannel);
      rdata[2] = 1;
      Array.Copy(enc.GetBytes("Artist Name"), 0, rdata, 3, 11);
      return rdata;
    }

    private byte[] songChanged()
    {
      byte[] rdata = new byte[35];
      rdata[0] = 0xd3;
      rdata[1] = 1; // (byte)rand.Next(maxChannel);
      rdata[2] = 1;
      //writeString(rdata, 3, "New Song Title " + rand.Next(100).ToString());
      return rdata;
    }

    private byte[] changeChannel(byte[] data)
    {
      channel = data[6];
      return new byte[] { 0x90, 0x02, channel, 0x00, 0x00, 0x99 };
    }

    virtual public void write(System.IO.Ports.SerialPort port, byte[] data)
    {
      int length = data.Length + 6;
      buffer = new byte[length];
      buffer[0] = 0x5a;
      buffer[1] = 0xa5;
      buffer[2] = (byte)(data.Length / 256);
      buffer[3] = (byte)(data.Length & 0x00ff);
      System.Array.Copy(data, 0, buffer, 4, data.Length);
      buffer[length - 2] = (byte)(dataLength / 256);
      buffer[length - 1] = (byte)(dataLength & 256);
      port.Write(buffer, 0, length);
      //displayBuffer("<", buffer);
    }

    public void displayBuffer(string prefix, byte[] buffer)
    {
      if (buffer != null)
      {
        int len = buffer.Length;
        Console.Write(prefix + " ");
        for (int i = 0; i < len; ++i)
        {
          Console.Write(buffer[i].ToString("X2"));
          Console.Write(' ');
        }
        Console.WriteLine();
      }
    }

  }

}
